#include <chrono>
#include <string>
#include <iostream>

namespace Chapter_6 {

    static std::chrono::steady_clock::time_point perf_log_t0;
    static std::chrono::steady_clock::time_point perf_log_t1;

    #define PERF_LOG_BEGIN perf_log_t0 = std::chrono::steady_clock::now();
    #define PERF_LOG_END   perf_log_t1 = std::chrono::steady_clock::now();
    #define PERF_COUNT std::chrono::duration_cast<std::chrono::milliseconds>(perf_log_t1 - perf_log_t0).count()

    class Rational_Default_Alloc {
        public:
            Rational_Default_Alloc (int a = 0, int b = 1) : n(a), d(b) {}

            static void show_test() {
                Rational_Default_Alloc *array[1000];
                PERF_LOG_BEGIN
                int i = 0;
                for (int j = 0; j < 500; j++) {
                    for (i = 0; i < 1000; i++) {
                        array[i] = new Rational_Default_Alloc(i);
                    }
                    for (i = 0; i < 1000; i++) {
                        delete []array[i];
                    }
                }
                PERF_LOG_END
                std::cout << "Time elapsed(ms): " << std::to_string(PERF_COUNT).c_str() << std::endl;
            }

        private:
            int n;     //numerator
            int d;     //denominator
    };

    class Rational_Pro_Alloc {
        enum { EXPANSION_SIZE = 32 };
        public:
            struct NextOnFreeList {
                NextOnFreeList *next;
            };
            Rational_Pro_Alloc (int a = 0, int b = 1) : n(a), d(b) {}

            static void show_test() {
                Rational_Pro_Alloc * array[1000];
                Rational_Pro_Alloc::newMemPool();
                PERF_LOG_BEGIN
                int i = 0;
                for (int j = 0; j < 500; j++) {
                    for (i = 0; i < 1000; i++) {
                        array[i] = new Rational_Pro_Alloc(i);
                    }
                    for (i = 0; i < 1000; i++) {
                        delete []array[i];
                    }
                }
                PERF_LOG_END
                std::cout << "Time elapsed(ms): " << std::to_string(PERF_COUNT).c_str() << std::endl;
                PERF_LOG_BEGIN
                Rational_Pro_Alloc::deleteMemPool();
                PERF_LOG_END
                std::cout << "Time elapsed(ms): " << std::to_string(PERF_COUNT).c_str() << std::endl;
            }

            inline void *operator new(size_t size) {
                if (nullptr == freeList) {
                    expandTheFreeList();
                }

                NextOnFreeList *head = freeList;
                freeList = head->next;

                return head;
            }

            inline void operator delete(void *doomed, size_t size) {
                NextOnFreeList *head = static_cast<NextOnFreeList *>(doomed);
                head->next = freeList;
                freeList = head;
            }

            static void newMemPool() {
                expandTheFreeList();
            }

            static void deleteMemPool() {
                NextOnFreeList *nextPtr;
                for (nextPtr = freeList; nextPtr != nullptr; nextPtr = freeList) {
                    freeList = freeList->next;
                    delete [] nextPtr;
                }
            }

        private:
            static NextOnFreeList *freeList;
            static void expandTheFreeList() {
                size_t size = (sizeof(Rational_Pro_Alloc) > sizeof(NextOnFreeList *)) ?
                    sizeof(Rational_Pro_Alloc) : sizeof(NextOnFreeList);

                NextOnFreeList *runner = reinterpret_cast<NextOnFreeList *>(new char[size]);
                freeList = runner;
                for (int i = 0; i < EXPANSION_SIZE; ++i) {
                    runner->next = reinterpret_cast<NextOnFreeList *>(new char[size]);
                    runner = runner->next;
                }
                runner->next = nullptr;
            }
            int n;  //numerator
            int d;  //denominator
    };

    Rational_Pro_Alloc::NextOnFreeList *Rational_Pro_Alloc::freeList = nullptr;
}

int main() {
    Chapter_6::Rational_Default_Alloc::show_test();
    Chapter_6::Rational_Pro_Alloc::show_test();
    return 0;
}